package study.test.demo.dom4j;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

/**
 * Created by zfh on 16-4-18.
 */
public class ChannelFileReader {
    private FileInputStream fileIn;
    private ByteBuffer byteBuf;
    private long fileLength;
    private int arraySize;
    private byte[] array;
    private String eleName = "";
    private String rowElementName; // 节点名称
//    static int i = 0;
    private Map<String, Object> titleMap = new LinkedHashMap<>();

    public ChannelFileReader(String fileName, int arraySize, String rowElementName) throws IOException {
        this.fileIn = new FileInputStream(fileName);
        this.fileLength = fileIn.getChannel().size();
        this.arraySize = arraySize;
        this.byteBuf = ByteBuffer.allocate(arraySize);
        this.rowElementName = rowElementName;
    }

    public int read() throws IOException, DocumentException {
        FileChannel fileChannel = fileIn.getChannel();
        int bytes = fileChannel.read(byteBuf);// 读取到ByteBuffer中
        if (bytes != -1) {
            array = new byte[bytes];// 字节数组长度为已读取长度
            byteBuf.flip(); // 读/写指针position指到缓冲区头部
            byteBuf.get(array);// 从ByteBuffer中得到字节数组
            eleName = eleName + new String(array, "UTF-8"); // 如果字符串100w查找不到节点则 继续向下查找。如果误操作节点， 暂时废弃
            String startName = "<" + rowElementName + ">";
            String endName = "</" + rowElementName + ">";
//            i++;
            // 如果存在完整的标签进行处理数据
            if (eleName.contains(startName) && eleName.contains(endName)) { // 如果包含闭合标签且包含开始标签，则认为通过
                eleName = StringUtils.substringBetween(eleName, startName, endName);
                // 然后只读取</ >里面的
                eleName = startName + eleName + endName; // 拼接为要处理的xml元素
                Document document = DocumentHelper.parseText(eleName);
                Element eleRoot = document.getRootElement();
                List<Element> eleList = eleRoot.elements();
                if (CollectionUtils.isNotEmpty(eleList)) {
                    for (Element ele : eleList) {
                        titleMap.put(ele.getName(), ele.getText());
                    }
                }
                byteBuf.clear();
                return -1;
            } else if (eleName.contains(startName) || eleName.contains(endName)) { // 如果包含闭合标签且包含开始标签，则认为通过
                // 如果存在部分标签，进行再次的读取
                byteBuf.clear();
                return bytes;
            } // 如果一个也不存在，不再进行读取
            byteBuf.clear();
            return -1;
        }
        return -1;
    }

    public void close() throws IOException {
        fileIn.close();
        array = null;
    }

    public byte[] getArray() {
        return array;
    }

    public long getFileLength() {
        return fileLength;
    }

    public static void main(String[] args) throws IOException, DocumentException {
        ChannelFileReader reader = new ChannelFileReader("F:\\2AsaveFile\\test.xml", 1048576, "RECORD1");
        long start = System.currentTimeMillis();
        while (reader.read() != -1)
            ;
//        reader.read(); // 暂时只读取1次
        long end = System.currentTimeMillis();
        System.out.println("获得的数据");
        Map<String, Object> titleMap = reader.getTitleMap();
        for (Entry<String, Object> e : titleMap.entrySet()) {
            System.out.println(e.getKey() + "===" + e.getValue());
        }
        System.out.println("map的长度" + titleMap.size());
//        System.out.println("总共读取了几次" + String.valueOf(i));
        reader.close();
        System.out.println("ChannelFileReader: " + (end - start));
    }

    public Map<String, Object> getTitleMap() {
        return titleMap;
    }

    public void setTitleMap(Map<String, Object> titleMap) {
        this.titleMap = titleMap;
    }
}
